package com.esperia09.webservice.mixibot.oauth;

import java.io.IOException;
import java.util.Collections;

import javax.cache.Cache;
import javax.cache.CacheException;
import javax.cache.CacheFactory;
import javax.cache.CacheManager;

import net.sf.json.JSONObject;

import com.google.gson.Gson;

/**
 * キャッシュされたトークンを取得
 * @author esperia
 *
 */
public class GetToken {
	private static final GetToken instance = new GetToken();
	// private static final Logger log = Logger.getLogger(GetToken.class.getName());
	private static final String REFRESH_TOKEN = "refresh_token";
	private static final String ACCESS_TOKEN = "access_token";
	private static final String EXPIRES_IN = "expires_in";
	
	public GetToken() {}
	public static GetToken getInstance() {
		return instance;
	}
	
	/**
	 * トークンを取得する
	 * @param authCode Authorization Code
	 * @return
	 * @throws IOException
	 */
	public String getAccessToken(String authCode) {
		// キャッシュよりアクセストークンを得る処理
		MixiTokenStruct useTokens = getCachedToken();
		try {
			if(useTokens.getAccess_token() != null) {
				return useTokens.getAccess_token();
			} else {
				if(useTokens.getRefresh_token() != null) {
					useTokens = jsonTokenParser(SendTokenRequest.refreshAccessToken(useTokens.getRefresh_token()));
					// TODO: 取得した結果がエラーだったときの処理
					cacheToken(useTokens);
					return useTokens.getAccess_token();
				} else {
					if(authCode != null) {
						useTokens = jsonTokenParser(SendTokenRequest.getNewTokens(authCode));
						// TODO: 取得した結果がエラーだったときの処理
						cacheToken(useTokens);
						return useTokens.getAccess_token();
					} else {
						// TODO: AuthorizationCodeの取得
						useTokens = null;
					}
				}
			}
		} catch(NullPointerException e) {
			// TODO: エラー処理
			// log.info(e.getMessage());
			System.out.println("GetToken::ぬるぽ。" + e.getMessage());
		} catch (IOException e) {
			// TODO: エラー処理
			// log.info(e.getMessage());
			System.out.println("GetToken::IOException" + e.getMessage());
		}
		
		return null;
	}
	/**
	 * MixiのTokenを解析し、MixiTokenStructに固めて返す
	 * @return MixiTokenStruct アクセストークンとリフレッシュトークン
	 */
	private MixiTokenStruct jsonTokenParser(String json) {
		JSONObject jsonObj = JSONObject.fromObject(json);
		// Gson gson = new Gson();
		// MixiTokenStruct tokens = gson.fromJson(json, MixiTokenStruct.class);
		
		MixiTokenStruct tokens = new MixiTokenStruct();
		tokens.setRefresh_token((String)jsonObj.get("refresh_token"));
		tokens.setAccess_token((String)jsonObj.get("access_token"));
		tokens.setExpires_in((String)jsonObj.get("expires_in"));
		// tokens.scope = (String)jsonObj.get("scope");
		// System.out.println(json); // for debug
		
		return tokens;
	}
	
	/**
	 * JCacheオブジェクトを呼び出す
	 * http://code.google.com/intl/ja/appengine/docs/java/memcache/usingjcache.html
	 * @return Cache JCacheオブジェクト
	 */
	private Cache getCache() {
		Cache cache = null;
		/* TODO: ExpireDateの設定
        Map props = new java.util.Map;
        props.put(GCacheFactory.EXPIRATION_DELTA, 3600);
        */
		try {
			CacheFactory cacheFactory = CacheManager.getInstance().getCacheFactory();
			cache = cacheFactory.createCache(Collections.emptyMap());
		} catch(CacheException e) {
			// log.info(e.getMessage());
			// TODO: エラー処理
		}

		return cache;
	}
	
	/**
	 * JCacheからトークンを取り出す
	 * @return
	 */
	private MixiTokenStruct getCachedToken() {
		Cache cache = getCache();
		MixiTokenStruct cacheTokens = new MixiTokenStruct();

		cacheTokens.setExpires_in((String)cache.get(EXPIRES_IN));
		cacheTokens.setRefresh_token((String)cache.get(REFRESH_TOKEN));
		cacheTokens.setAccess_token((String)cache.get(ACCESS_TOKEN));
		// System.out.println("refresh_token : " + cacheTokens.getRefresh_token() + " access_token : " + cacheTokens.getAccess_token());
		
		return cacheTokens;
	}
	/**
	 * トークンを保管する
	 * @param tokens
	 * @return
	 */
	private boolean cacheToken(MixiTokenStruct tokens) {
		Cache cache = getCache();

		try{
			cache.put(EXPIRES_IN, tokens.getExpires_in());
			cache.put(REFRESH_TOKEN, tokens.getRefresh_token());
			cache.put(ACCESS_TOKEN, tokens.getAccess_token());
		} catch(Exception e) {
			return false;
		}
		return true;
	}
}
